﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using Sustainalytics.ClientDW.DataAccessLayer;
using Sustainalytics.ClientDW.Entities;
using Sustainalytics.DataLayer.ConnectionStrings;
using Sustainalytics.DataLayer;
using Sustainalytics.DataLayer.Shared;
using Sustainalytics.Entities.ConnectionStrings;
using Sustainalytics.ESGRatings.BusinessLogic.ESGOnlineReport;
using Sustainalytics.ESGRatings.BusinessLogic.Models;
using Sustainalytics.Utils;
using MongoDB.Driver;
using Sustainalytics.ESG.Entities;
using Sustainalytics.MongoDB.Extensions;
using KeyEsgIssue = Sustainalytics.ESGRatings.BusinessLogic.Models.KeyEsgIssue;
using KeyEsgIssueIndicator = Sustainalytics.ESGRatings.BusinessLogic.Models.KeyEsgIssueIndicator;
using KeyEsgIssueIndicatorType = Sustainalytics.ESG.Entities.KeyEsgIssueIndicatorType;

namespace Sustainalytics.ESGRatings.BusinessLogic
{
    public class KeyEsgIssuesOnlineReport : IKeyEsgIssuesOnlineReport
    {
        private IClientDwRepository _clientDWRepository;
        private string _clientPlatform;
        private IConnectionStringsManager _connectionStringsManager;
        private string _clientDw;

        private IRepositoryCollection<CorporateDataBasic> _corporateDataRepository;
        private IKeyEsgIssuesRepository _keyEsgIssuesRepository;



        public IList<KeyEsgIssueValue> GetImpactDataByCompanyId(Guid companyId, IEnumerable<int> universeIds)
        {
            Init();

            CheckBelongsToUniverse(companyId, universeIds);

            var database = MongoDatabaseFactory.GetDatabase(_clientDw);
            var cdCollection = database.GetCollection<CorporateData>(typeof(CorporateData).Name);
            var hasCoreEsgResearch = cdCollection.Count(cd => cd.Id == companyId && cd.CoreEsgResearchState.ResearchState == BusinessResearchProductStates.Entities.ResearchState.ResearchState.ResearchAvailable) > 0;


            CompanyKeyESGIssue companyKeyEsgIssues = null;

            if (!hasCoreEsgResearch)
            {
                var collection = database.GetCollection<CompanyKeyESGIssue>(typeof(CompanyKeyESGIssue).Name);
                companyKeyEsgIssues = collection.Find(esg => esg.Id == companyId).SingleOrDefault();
            }


            var subPeerGroupCodeByCompanyId = companyKeyEsgIssues?.SubPeerGroupId ?? GetSubPeerGroupCodeByCompanyId(companyId);
            var subPeerGroupKeyEsgIssues = _keyEsgIssuesRepository.GetKeyEsgIssuesBySubPeerGroup(subPeerGroupCodeByCompanyId)
                                            .OrderBy(p => p.BussinessImpact)
                                            .ThenByDescending(p => p.SustainabilityImpact)
                                            .ToList();

            if (subPeerGroupKeyEsgIssues.IsNullOrEmpty())
            {
                throw new ESGReasearchNotFoundException();
            }

            var result = subPeerGroupKeyEsgIssues.Select(impactValue => new KeyEsgIssueValue
            {
                KeyEsgIssue = new KeyEsgIssue
                {
                    Id = impactValue.SortKey,
                    Name = impactValue.Name,
                    Description = impactValue.Description,
                    Selected =
                        companyKeyEsgIssues != null &&
                        companyKeyEsgIssues.KeyESGIssues.Any(key => key.ExternalId == impactValue.ExternalId)
                },
                BusinessImpact = impactValue.BussinessImpact,
                SustainabilityImpact = impactValue.SustainabilityImpact,
                BusinessImpactLevel = KeyEsgIssueValueLevelEnum.Low,
                SustainabilityImpactLevel = KeyEsgIssueValueLevelEnum.Low
            }).ToList();

            return result;
        }

        public IList<KeyEsgIssueAssessment> GetKeyEsgIssuesByCompanyId(Guid companyId, IEnumerable<int> universeIds)
        {
            Init();
            CheckBelongsToUniverse(companyId, universeIds);

            var companyKeyEsgIssues = _keyEsgIssuesRepository.GetKeyEsgIssuesByCompanyId(companyId);

            if (companyKeyEsgIssues == null || companyKeyEsgIssues.KeyESGIssues.IsNullOrEmpty())
            {
                throw new ESGReasearchNotFoundException();
            }

            var orderedKeyEsgIssues = companyKeyEsgIssues.KeyESGIssues.OrderBy(x => x.SortKey?.Value);

            var result = (from companyKeyEsgIssue in orderedKeyEsgIssues
                          let keyEsgIssue = new KeyEsgIssue
                          {
                              Id = companyKeyEsgIssue.SortKey,
                              Name = companyKeyEsgIssue.Name,
                              Description = companyKeyEsgIssue.Description,
                              Selected = true
                          }
                          select new KeyEsgIssueAssessment
                          {
                              AssessmentText = companyKeyEsgIssue.Comment,
                              KeyEsgIssue = keyEsgIssue
                          }).ToList();

            return result;
        }

        public IList<KeyEsgIssueIndicator> GetStatisticalDataByCompanyId(Guid companyId, IEnumerable<int> universeIds)
        {
            Init();
            CheckBelongsToUniverse(companyId, universeIds);
            var companyKeyEsgIssues = _keyEsgIssuesRepository.GetKeyEsgIssuesByCompanyId(companyId);
            var themeAndCategoryIds = _clientDWRepository.ListThemeAndCategoryIdPerIndicatorNumber();

            var data = new List<KeyEsgIssueIndicator>();
            if (companyKeyEsgIssues != null)
            {
                // create list of the company's 3 key esg issues
                foreach (
                    var issue in companyKeyEsgIssues.KeyESGIssues.OrderBy(x=> x.SortKey?.Value).Where(issue => issue.KeyEsgIssueIndicators != null))
                {
                    data.AddRange(issue.KeyEsgIssueIndicators
                        .Where(
                            i =>
                                i.Type == ESG.Entities.KeyEsgIssueIndicatorType.EventIndicator || i.HidenToClient ||
                                !StringComparer.InvariantCultureIgnoreCase.Equals(i.Score, "N/A"))
                        // exclue RP indicators for which the score is N/A
                        .Select(indicator => new KeyEsgIssueIndicator
                        {
                            KeyEsgIssue = new KeyEsgIssue
                            {
                                Id = issue.SortKey,
                                Name = issue.Name,
                                Selected = true
                            },
                            IsEventIndicator = indicator.Type == KeyEsgIssueIndicatorType.EventIndicator,
                            Name = " " + indicator.Name,
                            Number = indicator.Number,
                            IsControversyIndicator = indicator.IsControversyIndicator,
                            IsRpIndicator = indicator.Type == KeyEsgIssueIndicatorType.StandardIndicator,
                            RpIndicatorScore =
                                indicator.Type == KeyEsgIssueIndicatorType.StandardIndicator
                                    ? new KeyEsgIssueRpIndicatorScore
                                    {
                                        BestPractice =
                                            indicator.HidenToClient
                                                ? "--"
                                                : string.Equals(indicator.BestPractice, "N/A")
                                                    ? "--"
                                                    : indicator.BestPractice,
                                        CompanyScore =
                                            indicator.HidenToClient
                                                ? "Contact Your Client Advisor"
                                                : indicator.Score,
                                        PeerGroupAverage =
                                            indicator.HidenToClient
                                                ? "--"
                                                : string.Equals(indicator.Average, "N/A") ? "--" : indicator.Average,
                                        MaterialBestPracticeGap =
                                            indicator.HidenToClient
                                                ? KeyEsgIssueIndicatorMaterialBestPracticeGap.NA
                                                : (KeyEsgIssueIndicatorMaterialBestPracticeGap)
                                                    indicator.MaterialBestPracticeGap
                                    }
                                    : null,
                            EventIndicatorScore =
                                indicator.Type == KeyEsgIssueIndicatorType.EventIndicator
                                    ? new KeyEsgIssueEventIndicatorScore
                                    {
                                        BestPractice = indicator.BestPractice,
                                        //Category = indicator.HidenToClient ? "Research Underway" : indicator.Average,
                                        PeerGroupAverage = indicator.Average,
                                        MaterialBestPracticeGap =
                                            (KeyEsgIssueIndicatorMaterialBestPracticeGap)
                                                indicator.MaterialBestPracticeGap,
                                        AnswerCategory = indicator.AnswerCategory
                                    }
                                    : null,
                            Id =
                                indicator.Type == KeyEsgIssueIndicatorType.StandardIndicator
                                    ? indicator.Number
                                    : string.Empty,
                            IndicatorTheme = indicator.Type == KeyEsgIssueIndicatorType.StandardIndicator
                                ? themeAndCategoryIds.ContainsKey(indicator.Number)
                                    ? themeAndCategoryIds[indicator.Number].Key
                                    : string.Empty
                                : string.Empty,
                            IndicatorCategoryId = indicator.Type == KeyEsgIssueIndicatorType.StandardIndicator
                                ? themeAndCategoryIds.ContainsKey(indicator.Number)
                                    ? themeAndCategoryIds[indicator.Number].Value
                                    : 0
                                : 0
                        }));
                }
            }

            if (HasCoreEsgResearch(companyId))
            {
                data.Sort(Models.KeyEsgIssueIndicatorSorter.Sort);
            }


            return data;
        }

        private void CheckBelongsToUniverse(Guid companyId, IEnumerable<int> universeIds)
        {
            if (universeIds == null)
            {
                throw new ArgumentNullException(nameof(universeIds));
            }
            _corporateDataRepository =
                new RepositoryCollection<CorporateDataBasic>(
                    _connectionStringsManager.GetConnectionString(ConnectionStringType.ClientDW), "CorporateData");
            var corporateData = _corporateDataRepository.ReadWhere(x => x.Id == companyId, 0, 1, null, x => x.UniverseIds).FirstOrDefault();
            if (corporateData == null || !corporateData.UniverseIds.Intersect(universeIds).Any())
            {
                throw new ESGReasearchNotInUniversesException();
            }
        }

        private bool HasCoreEsgResearch(Guid companyId)
        {
            var corporateDataRepository = new RepositoryCollection<CorporateData>(_connectionStringsManager.GetConnectionString(ConnectionStringType.ClientDW));
            var corporateData = corporateDataRepository.ReadWhere(x => x.Id == companyId, 0, 1, null, x => x.CoreEsgResearchState).FirstOrDefault();

            return corporateData != null && corporateData.CoreEsgResearchState.ResearchState == BusinessResearchProductStates.Entities.ResearchState.ResearchState.ResearchAvailable;
        }

        private void Init()
        {
            _clientPlatform = ConfigurationManager.ConnectionStrings["ClientPlatform"].ConnectionString;
            _connectionStringsManager =
                ConnectionStringsManager.GetInstance(ConnectionStringsRepository.GetInstance(_clientPlatform));

            _clientDw = _connectionStringsManager.GetConnectionString(ConnectionStringType.ClientDW);


            _keyEsgIssuesRepository = KeyEsgIssuesRepository.GetInstance(_clientDw);
            _clientDWRepository = ClientDwRepository.GetInstance(_clientDw);

        }

        private int GetSubPeerGroupCodeByCompanyId(Guid companyId)
        {
            return MongoFactory.GetCollectionFromDatabase<CorporateData>(_clientDw)
                .Find(cd => cd.Id == companyId)
                .Project(cd => cd.SubPeerGroupId)
                .FirstOrDefault();
        }

    }
}